package tech.cn.note.excel;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.PostConstruct;

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.jexl3.JexlBuilder;
import org.apache.commons.jexl3.JexlEngine;
import org.jxls.common.Context;
import org.jxls.transform.TransformationConfig;
import org.jxls.transform.Transformer;
import org.jxls.util.JxlsHelper;

import org.springframework.stereotype.Service;

@Service
public class JXLSTemplateDataFillServiceImpl implements JXLSTemplateDataFillService {

    private JxlsHelper jxlsHelper;

    private JexlEngine jexlEngine;

    @PostConstruct
    private void JXLSProcessInit() {
        jxlsHelper = JxlsHelper.getInstance();
        Map<String, Object> functionMap = new HashMap<>();
        functionMap.put(JXLSFunction.FUNCTION_NAME, JXLSFunction.getInstance());
        JexlBuilder jexlBuilder = new JexlBuilder();
        jexlBuilder.namespaces(functionMap);
        jexlEngine = jexlBuilder.create();
    }

    @Override
    public byte[] processToByte(File templateFile, Map<String, Object> dataSource) throws IOException {
        FileInputStream inputStream = new FileInputStream(templateFile);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        doProcess(inputStream, outputStream, dataSource);
        return outputStream.toByteArray();
    }

    @Override
    public byte[] processToByte(InputStream inputStream, Map<String, Object> dataSource) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        doProcess(inputStream, outputStream, dataSource);
        return outputStream.toByteArray();
    }

    @Override
    public byte[] processToPdfByte(File templateFile, Map<String, Object> dataSource) throws Exception {
        byte[] dataByte = processToByte(templateFile, dataSource);
        return null; // todo FileUtil.doPdfProcessByAsposeOfExcel(dataByte);
    }

    @Override
    public void writeToFile(File templateFile, File outFile, Map<String, Object> dataSource) throws Exception {
        byte[] dataByte = processToByte(templateFile, dataSource);
        FileOutputStream fileOutputStream = new FileOutputStream(outFile);
        fileOutputStream.write(dataByte);
        fileOutputStream.close();
    }

    @Override
    public void writeToOutputStream(File templateFile, OutputStream outPut, Map<String, Object> dataSource)
            throws IOException {
        FileInputStream inputStream = new FileInputStream(templateFile);
        doProcess(inputStream, outPut, dataSource);
    }

    @Override
    public void writeToOutputStream(InputStream inputStream, OutputStream outPut, Map<String, Object> dataSource)
            throws IOException {
        doProcess(inputStream, outPut, dataSource);
    }

    /**
     * 根据模板将数据填充至输出流中
     *
     * @param inputStream
     * @param outputStream
     * @param dataSource
     */
    private synchronized void doProcess(InputStream inputStream,
            OutputStream outputStream,
            Map<String, Object> dataSource) throws  IOException {
        Context dataContext = new Context();
        if (MapUtils.isNotEmpty(dataSource)) {
            dataSource.forEach(dataContext::putVar);
        }
        Transformer transformer = jxlsHelper.createTransformer(inputStream, outputStream);
        transformer.getTransformationConfig().setExpressionEvaluator(new CustomJexlExpressionEvaluator());
        TransformationConfig transformationConfig = transformer.getTransformationConfig();
        CustomJexlExpressionEvaluator evaluator = (CustomJexlExpressionEvaluator) transformationConfig.getExpressionEvaluator();
        evaluator.setJexlEngine(jexlEngine);
        jxlsHelper.setUseFastFormulaProcessor(false).processTemplate(dataContext, transformer);
        IOUtils.closeQuietly(inputStream, outputStream);
    }
}
